/* SPDX-License-Identifier: MIT */

#include "tunables.h"
#include "adt.h"
#include "pmgr.h"
#include "soc.h"
#include "types.h"
#include "utils.h"

/*
 * These magic tunable sequences are hardcoded in various places in XNU, and are required for
 * proper operation of various fabric features and other miscellanea. Without them, things tend
 * to subtly break...
 */

struct entry {
    u32 offset;
    u32 clear;
    u32 set;
};

struct sequence {
    struct entry *entry;
    u64 base;
};

struct entry t8103_agx_entry[] = {
    {0x30, 0xffffffff, 0x50014},     {0x34, 0xffffffff, 0xa003c},
    {0x400, 0x400103ff, 0x40010001}, {0x600, 0x1ffffff, 0x1ffffff},
    {0x738, 0x1ff01ff, 0x140034},    {0x798, 0x1ff01ff, 0x14003c},
    {0x800, 0x100, 0x100},           {-1, 0, 0},
};

struct sequence t8103_agx_tunables[] = {
    {t8103_agx_entry, 0x205000000},
    {NULL, -1},
};

// TODO: check masks
struct entry t600x_agx_entry[] = {
    {0x0, 0x1, 0x1},
    {0x10, 0xfff0000, 0xd0000},
    {0x14, 0x3, 0x1},
    {0x18, 0x3, 0x1},
    {0x1c, 0x3, 0x3},
    {0x20, 0x3, 0x3},
    {0x24, 0x3, 0x3},
    {0x28, 0x3, 0x3},
    {0x2c, 0x3, 0x3},
    {0x400, 0x400103ff, 0x40010001},
    {0x600, 0x1ffffff, 0x1ffffff},
    {0x800, 0x100, 0x100},
    {-1, 0, 0},
};

struct sequence t600x_agx_tunables[] = {
    {t600x_agx_entry, 0x405000000},
    {NULL, -1},
};

// TODO: check masks
struct entry t8112_agx_entry[] = {
    {0x0, 0x200, 0x200},
    {0x34, 0xffffffff, 0x50014},
    {0x38, 0xffffffff, 0xa003c},
    {0x400, 0xc00103ff, 0xc0010001},
    {0x600, 0x1ffffff, 0x1ffffff},
    {0x738, 0x1ff01ff, 0x14003c},
    {0x798, 0x1ff01ff, 0x14003c},
    {0x800, 0x100, 0x100},
    {-1, 0, 0},
};

struct sequence t8112_agx_tunables[] = {
    {t8112_agx_entry, 0x205000000},
    {NULL, -1},
};

struct entry t8103_ane_pmgr_entry[] = {
    {0x0, 0x10, 0x10},
    {0x38, 0xffff, 0x50020},
    {0x3c, 0xffff, 0xa0030},
    {0x400, 0x4, 0x40010001},
    {0x600, 0x0, 0x1ffffff},
    {0x738, 0x1ff01ff, 0x200020},
    {0x798, 0x1ff01ff, 0x100030},
    {0x7f8, 0x1ff01ff, 0x100000a},
    {0x900, 0x1, 0x101},
    {-1, 0, 0},
};

struct entry t8103_ane_dart_entry[] = {
    {0xfc, 0x0, 0x1},         {0x2d0, 0x0, 0x0},
    {0x2f0, 0x0, 0x0},        {0x34, 0x0, 0xffffffff},
    {0x20, 0x0, 0x100000},    {0x60, 0x0, 0x80016100},
    {0x68, 0x20202, 0xf0f0f}, {0x6c, 0x0, 0x80808},
    {0x100, 0x0, 0x80},       {-1, 0, 0},
};

struct entry t8103_ane_dapf_entry[] = {
    {0x4, 0x0, 0x1},  {0x8, 0x0, 0x824000},    {0xc, 0x0, 0x8},  {0x10, 0x0, 0x8c7fff},
    {0x14, 0x0, 0x8}, {0x0, 0x0, 0x11},        {0x44, 0x0, 0x1}, {0x48, 0x0, 0x0},
    {0x4c, 0x0, 0xf}, {0x50, 0x0, 0xffffffff}, {0x54, 0x0, 0xf}, {0x40, 0x0, 0x33},
    {0x84, 0x0, 0x1}, {0x88, 0x0, 0x2b45c000}, {0x8c, 0x0, 0x2}, {0x90, 0x0, 0x2b45c003},
    {0x94, 0x0, 0x2}, {0x80, 0x0, 0x31},       {0xc4, 0x0, 0x1}, {0xc8, 0x0, 0x3b70c000},
    {0xcc, 0x0, 0x2}, {0xd0, 0x0, 0x3b70c033}, {0xd4, 0x0, 0x2}, {0xc0, 0x0, 0x31},
    {-1, 0, 0},
};

struct entry t8103_ane_dpe_sys_entry[] = {
    {0x3c, 0x0, 0x262}, {0x40, 0x0, 0x262}, {0x44, 0x0, 0x2c8}, {0x48, 0x0, 0x351},
    {0x4c, 0x0, 0x391}, {0x50, 0x0, 0x404}, {0x54, 0x0, 0x441}, {0x58, 0x0, 0x4b5},
    {0x5c, 0x0, 0x4ec}, {0x60, 0x0, 0x599}, {0x64, 0x0, 0x647}, {0x68, 0x0, 0x6f2},
    {0x6c, 0x0, 0x82b}, {0x70, 0x0, 0x82b}, {0x74, 0x0, 0x82b}, {0x78, 0x0, 0x82b},
    {-1, 0, 0},
};

struct entry t8103_ane_perf_entry[] = {
    {0x8, 0xff, 0xf8a96},   {0xc, 0xff, 0x11ca11},
    {0x10, 0xff, 0x15c4ad}, {0x14, 0xff, 0x1cafc0},
    {0x18, 0xff, 0x1c858e}, {0x1c, 0xff, 0x20cf3d},
    {0x20, 0xff, 0x2087b1}, {0x24, 0xff, 0x294c83},
    {0x28, 0xff, 0x2b6ffb}, {0x2c, 0xff, 0x2cfb4c},
    {0x30, 0xff, 0x305c9b}, {0x34, 0xff, 0x2e277f},
    {0x38, 0xff, 0x0},      {0x3c, 0xff, 0x0},
    {0x40, 0xff, 0x0},      {0x48, 0xff, 0xd47e2},
    {0x4c, 0xff, 0x11ca11}, {0x50, 0xff, 0x15c4ad},
    {0x54, 0xff, 0x1a1c07}, {0x58, 0xff, 0x1c858e},
    {0x5c, 0xff, 0x20cf3d}, {0x60, 0xff, 0x2087b1},
    {0x64, 0xff, 0x270050}, {0x68, 0xff, 0x28cacd},
    {0x6c, 0xff, 0x2b19cc}, {0x70, 0xff, 0x2b09f6},
    {0x74, 0xff, 0x2cd4e0}, {0x78, 0xff, 0x0},
    {0x7c, 0xff, 0x0},      {0x80, 0xff, 0x0},
    {0x88, 0xff, 0xb8130},  {0x8c, 0xff, 0xf5f41},
    {0x90, 0xff, 0x12b938}, {0x94, 0xff, 0x167287},
    {0x98, 0xff, 0x14710e}, {0x9c, 0xff, 0x18b58d},
    {0xa0, 0xff, 0x147c37}, {0xa4, 0xff, 0x1635ee},
    {0xa8, 0xff, 0x18560a}, {0xac, 0xff, 0x18737e},
    {0xb0, 0xff, 0x19e10d}, {0xb4, 0xff, 0x0},
    {0xb8, 0xff, 0x0},      {0xbc, 0xff, 0x0},
    {0xc0, 0xff, 0x0},      {-1, 0, 0},
};

struct entry t8103_ane_dpe_soc_entry[] = {
    {0x24, 0x0, 0x226},  {0x28, 0x0, 0x6b7},  {0x2c, 0x0, 0xd8},   {0x30, 0x0, 0x1af},
    {0x34, 0x0, 0x35e},  {0x38, 0x0, 0x6bb},  {0x3c, 0x0, 0x226},  {0x40, 0x0, 0x6b7},
    {0x44, 0x0, 0xd8},   {0x48, 0x0, 0x1af},  {0x4c, 0x0, 0x35e},  {0x50, 0x0, 0x6bb},
    {0x54, 0x0, 0x21c},  {0x58, 0x0, 0x69a},  {0x5c, 0x0, 0xd4},   {0x60, 0x0, 0x1a8},
    {0x64, 0x0, 0x34f},  {0x68, 0x0, 0x69e},  {0x6c, 0x0, 0x213},  {0x70, 0x0, 0x67c},
    {0x74, 0x0, 0xd0},   {0x78, 0x0, 0x1a0},  {0x7c, 0x0, 0x340},  {0x80, 0x0, 0x680},
    {0x84, 0x0, 0x210},  {0x88, 0x0, 0x671},  {0x8c, 0x0, 0xcf},   {0x90, 0x0, 0x19e},
    {0x94, 0x0, 0x33b},  {0x98, 0x0, 0x675},  {0x9c, 0x0, 0x20b},  {0xa0, 0x0, 0x664},
    {0xa4, 0x0, 0xcd},   {0xa8, 0x0, 0x19a},  {0xac, 0x0, 0x334},  {0xb0, 0x0, 0x668},
    {0xb4, 0x0, 0x20b},  {0xb8, 0x0, 0x662},  {0xbc, 0x0, 0xcd},   {0xc0, 0x0, 0x19a},
    {0xc4, 0x0, 0x333},  {0xc8, 0x0, 0x666},  {0xcc, 0x0, 0x20e},  {0xd0, 0x0, 0x66d},
    {0xd4, 0x0, 0xcf},   {0xd8, 0x0, 0x19d},  {0xdc, 0x0, 0x339},  {0xe0, 0x0, 0x671},
    {0xe4, 0x0, 0x212},  {0xe8, 0x0, 0x67a},  {0xec, 0x0, 0xd0},   {0xf0, 0x0, 0x1a0},
    {0xf4, 0x0, 0x33f},  {0xf8, 0x0, 0x67e},  {0xfc, 0x0, 0x22d},  {0x100, 0x0, 0x6cc},
    {0x104, 0x0, 0xda},  {0x108, 0x0, 0x1b4}, {0x10c, 0x0, 0x368}, {0x110, 0x0, 0x6d0},
    {0x114, 0x0, 0x25f}, {0x118, 0x0, 0x768}, {0x11c, 0x0, 0xee},  {0x120, 0x0, 0x1dc},
    {0x124, 0x0, 0x3b7}, {0x128, 0x0, 0x76d}, {0x12c, 0x0, 0x2ab}, {0x130, 0x0, 0x857},
    {0x134, 0x0, 0x10c}, {0x138, 0x0, 0x217}, {0x13c, 0x0, 0x42e}, {0x140, 0x0, 0x85c},
    {0x144, 0x0, 0x384}, {0x148, 0x0, 0xafd}, {0x14c, 0x0, 0x161}, {0x150, 0x0, 0x2c2},
    {0x154, 0x0, 0x583}, {0x158, 0x0, 0xb05}, {0x15c, 0x0, 0x384}, {0x160, 0x0, 0xafd},
    {0x164, 0x0, 0x161}, {0x168, 0x0, 0x2c2}, {0x16c, 0x0, 0x583}, {0x170, 0x0, 0xb05},
    {0x174, 0x0, 0x384}, {0x178, 0x0, 0xafd}, {0x17c, 0x0, 0x161}, {0x180, 0x0, 0x2c2},
    {0x184, 0x0, 0x583}, {0x188, 0x0, 0xb05}, {0x18c, 0x0, 0x384}, {0x190, 0x0, 0xafd},
    {0x194, 0x0, 0x161}, {0x198, 0x0, 0x2c2}, {0x19c, 0x0, 0x583}, {0x1a0, 0x0, 0xb05},
    {-1, 0, 0},
};

struct sequence t8103_ane_tunables[] = {
    {t8103_ane_pmgr_entry, 0x26a000000},
    {t8103_ane_dart_entry, 0x26b800000},
    {t8103_ane_dart_entry, 0x26b810000},
    {t8103_ane_dart_entry, 0x26b820000},
    {t8103_ane_dapf_entry, 0x26b804000},
    {t8103_ane_dpe_sys_entry, 0x26b8f0000},
    {t8103_ane_perf_entry, 0x26b908000},
    {t8103_ane_dpe_soc_entry, 0x26b8f4000},
    {NULL, -1},
};

static void tunables_apply(struct sequence *seq)
{
    struct entry *entry = seq->entry;
    while (entry->offset != UINT32_MAX) {
        mask32(seq->base + entry->offset, entry->clear, entry->set);
        entry++;
    }
}

int power_and_apply(const char *path, struct sequence *seqs)
{
    if (pmgr_adt_power_enable(path) < 0) {
        printf("tunables: Failed to enable power: %s\n", path);
        return -1;
    }

    while (seqs->base != UINT64_MAX) {
        tunables_apply(seqs);
        seqs++;
    }

    if (pmgr_adt_power_disable(path) < 0) {
        printf("tunables: Failed to disable power: %s\n", path);
        return -1;
    }

    return 0;
}

int tunables_apply_static(void)
{
    int ret = 0;

    switch (chip_id) {
        case T8103:
            ret |= power_and_apply("/arm-io/sgx", t8103_agx_tunables);
            ret |= power_and_apply("/arm-io/ane", t8103_ane_tunables);
            break;
        case T8112:
            ret |= power_and_apply("/arm-io/sgx", t8112_agx_tunables);
            break;
        case T6000:
        case T6001:
        case T6002:
            ret |= power_and_apply("/arm-io/sgx", t600x_agx_tunables);
            break;
        default:
            break;
    }

    return ret ? -1 : 0;
}
